#include "ModelVBO.h"
#include "base.h"
#include "Types.h"
ModelVBO::ModelVBO()
{
	m_nElementCount = 0;
	m_validator = boost::shared_ptr<int>(new int(0));
	m_bHasNormal = false;
	m_bHasTexCoord = false;
}
ModelVBO::~ModelVBO()
{
	Destroy();
}
bool ModelVBO::Create(const vector3 *pVertexData, const vector3 *pNormalData, const vector2 *pTexCoordData, uint count)
{
	Destroy();

	if(pVertexData == NULL) {
		return false;
	}

	uint vertDataSize = sizeof(vector3) * count;
	uint texDataSize = 0;
	uint normDataSize = 0;

	if(pNormalData != NULL) {
		normDataSize = sizeof(vector3) * count;
		m_bHasNormal = true;
	} else {
		m_bHasNormal = false;
	}
	if(pTexCoordData != NULL){
		texDataSize = sizeof(vector2) * count;
		m_bHasTexCoord = true;
	} else {
		m_bHasTexCoord = false;
	}

	//Full data size
	uint arraySize = vertDataSize+normDataSize+texDataSize;

	glGenBuffers(1, &m_nVertexBufferObject);
	glBindBuffer(GL_ARRAY_BUFFER, m_nVertexBufferObject);
	glBufferData(GL_ARRAY_BUFFER, arraySize, NULL, GL_STATIC_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER, 0, vertDataSize, pVertexData);
	if(m_bHasNormal)
	{
		glBufferSubData(GL_ARRAY_BUFFER, vertDataSize, normDataSize, pNormalData);
	}
	if(m_bHasTexCoord)
	{
		glBufferSubData(GL_ARRAY_BUFFER, vertDataSize + normDataSize, texDataSize, pTexCoordData);
	}

	int bufferSize = 0;
	glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufferSize);
	if(bufferSize != arraySize)
	{
		return false;
	}

	m_vertPointer = (void*)0;
	m_normPointer = (void*)vertDataSize;
	m_texPointer = (void*)(vertDataSize + normDataSize);

	m_nElementCount = count;

	*m_validator = 1;
	return true;
}
void ModelVBO::Destroy()
{
	if(IsValid())
	{
		glDeleteBuffers(1, &m_nVertexBufferObject);
		*m_validator = 0;
	}
}
bool ModelVBO::IsValid()
{
	return *m_validator == 1;
}
void ModelVBO::Bind()
{
	glBindBuffer(GL_ARRAY_BUFFER, m_nVertexBufferObject);

	//Enable vertex array
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, m_vertPointer);

	//Enable other array states
	if(m_bHasNormal)
	{ 
		glEnableClientState(GL_NORMAL_ARRAY); 
		glNormalPointer(GL_FLOAT, 0, m_normPointer);
	}
	if(m_bHasTexCoord) 
	{ 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
		glTexCoordPointer(2, GL_FLOAT, 0, m_texPointer); 
	}
}
void ModelVBO::SetCustomPointers(int vertexPointer, int normalPointer, int texPointer)
{
	in_vertexPointer = vertexPointer;
	in_normalPointer = normalPointer;
	in_texPointer = texPointer;
}
void ModelVBO::CustomBind()
{
	glBindBuffer(GL_ARRAY_BUFFER, m_nVertexBufferObject);

	//Enable vertex array
	glEnableVertexAttribArray(in_vertexPointer);
	glVertexAttribPointer(in_vertexPointer, 3, GL_FLOAT, false, 0, m_vertPointer);

	//Enable other array states
	if(m_bHasNormal && (in_normalPointer != -1))
	{ 
		glEnableVertexAttribArray(in_normalPointer);
		glVertexAttribPointer(in_normalPointer, 3, GL_FLOAT, true, 0, m_normPointer);
	}
	if(m_bHasTexCoord && (in_texPointer != -1)) 
	{ 
		glEnableVertexAttribArray(in_texPointer);
		glVertexAttribPointer(in_texPointer, 2, GL_FLOAT, false, 0, m_texPointer);
	}
}
void ModelVBO::CustomUnbind()
{
	glBindBuffer(in_vertexPointer, 0);
	glDisableVertexAttribArray(in_vertexPointer);
	if(m_bHasNormal  && (in_normalPointer != -1))
	{ 
		glDisableVertexAttribArray(in_normalPointer); 
	}
	if(m_bHasTexCoord  && (in_texPointer != -1)) 
	{ 
		glDisableVertexAttribArray(in_texPointer); 
	}
}
void ModelVBO::Unbind()
{
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glDisableClientState(GL_VERTEX_ARRAY);
	if(m_bHasNormal)
	{ 
		glDisableClientState(GL_NORMAL_ARRAY); 
	}
	if(m_bHasTexCoord) 
	{ 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY); 
	}
}